using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Xml;

using libsecondlife;

namespace Feathertail
{
    public abstract class FeathertailBot
    {
        protected SecondLife  _SL;
        protected XmlDocument _BotConfig;

        public FeathertailBot(XmlDocument config)
        {
            _SL = new SecondLife();
            _BotConfig = config;
        }

        public abstract void InitPre();
        public virtual void InitPost()
        {
            // Set Appearance
            _SL.Appearance.SetPreviousAppearance(false);

            // Setup inventory 
            _SL.Inventory.RequestFolderContents(_SL.Inventory.Store.RootFolder.UUID, _SL.Self.AgentID, true, true, InventorySortOrder.FoldersByName);

            // If a bot location has been specified, try to get there
            XmlNodeList nl = _BotConfig.GetElementsByTagName("BotLocation");
            if (nl.Count != 0)
            {
                XmlElement BotLocation = (XmlElement)nl[0];

                // Can teleport or move to someplace here...
                int X, Y, Z;
                if ((int.TryParse(BotLocation.GetAttribute("x"), out X)) || (int.TryParse(BotLocation.GetAttribute("y"), out Y)) || (int.TryParse(BotLocation.GetAttribute("z"), out Z)))
                {
                    Console.WriteLine("Error parsing bot location information.  Will not teleport.");
                }
                else
                {
                    ManualResetEvent tpComplete = new ManualResetEvent(false);
                    AgentManager.TeleportCallback tpcb = delegate(string message, AgentManager.TeleportStatus status, AgentManager.TeleportFlags flags)
                    {
                        switch (status)
                        {
                            case AgentManager.TeleportStatus.Cancelled:
                            case AgentManager.TeleportStatus.Failed:
                                Console.WriteLine("Teleport did not complete, status: " + status);
                                tpComplete.Set();
                                break;
                            case AgentManager.TeleportStatus.Finished:
                                tpComplete.Set();
                                break;
                        }
                    };

                    _SL.Self.OnTeleport += tpcb;

                    _SL.Self.Teleport(BotLocation.GetAttribute("region"), new LLVector3(X, Y, Z));

                    if (tpComplete.WaitOne(30000, false))
                    {
                        Console.WriteLine("Teleport timed out");
                    }

                    _SL.Self.OnTeleport -= tpcb;

                }

            }

        }

        public void Run()
        {
            // Pre-connection initialization
            Console.WriteLine("Pre-connection initialization");
            InitPre();

            // Setup connect event 
            ManualResetEvent Connected = new ManualResetEvent(false);
            _SL.Network.OnConnected += delegate { Connected.Set(); };

            // Setup logout event
            ManualResetEvent ExitAppEvent = new ManualResetEvent(false);
            _SL.Network.OnLogoutReply += delegate { ExitAppEvent.Set(); };




            // Login to Second Life
            Console.WriteLine("Loggign In");
            XmlElement LoginInfo = (XmlElement)_BotConfig.GetElementsByTagName("LoginInfo")[0];
            XmlElement BotInfo = (XmlElement)_BotConfig.GetElementsByTagName("BotInfo")[0];
            if (!_SL.Network.Login(LoginInfo.GetAttribute("first"), LoginInfo.GetAttribute("last"), LoginInfo.GetAttribute("password"), LoginInfo.GetAttribute("name"), BotInfo.GetAttribute("email")))
            {
                // Login failed
                Console.WriteLine("Error logging in: " + _SL.Network.LoginMessage);
                return;
            }

            // Waiting to connect
            Console.WriteLine("Waiting to connect");
            if (Connected.WaitOne(new TimeSpan(0, 3, 0), false) == false)
            {
                Console.WriteLine("Time-out connecting.");
                return;
            }

            // Connected, do post init, and start the mainloop
            Console.WriteLine("Post-connect initialization");
            InitPost();

            Console.WriteLine("Starting Main Loop");
            MainLoop();


            // Wait until exit app event is raised, before actually exitting...
            ExitAppEvent.WaitOne();

        }

        public virtual void MainLoop()
        {
            Console.WriteLine("Press Enter to logout");
            Console.ReadLine();

            _SL.Network.Logout();
        }
    }
}
